﻿using System;
using System.Collections.Generic;
//using System.Text;

namespace Nemerle.Xml
{
  using Namespace;

  /// <summary>
  /// Describes Element, Text or Whitespace
  /// </summary>
  public variant Node : ElementBase
  {
    | Element {
                ns     : Namespace;
                prefix : string;
                name   : string;
                attrs  : list[Attr];
                nodes  : list[Node];

                public this(name : string, attrs : list[Attr], nodes : list[Node])
                {
                  this(Empty(), "", name, attrs, nodes);
                }
              }

    | Text       { value : string; }
    | Whitespace { value : string; }

    public override Transform(transform : ElementBase -> ElementBase) : ElementBase
    {
      match (this)
      {
        | Text | Whitespace => transform(this)
        | Element =>

          def res = transform(this);
          match (res)
          {
            | Element(ns, prefix, name, attrs, nodes) =>
                def result = Utils.TransformList(nodes, transform);

                if (result.TryForAll2(nodes, _refCmpFunc))
                  res
                else
                  Element(ns, prefix, name, attrs, result)

            | _ => res
          }
      }
    }

    public override GetHashCode() : int
    {
      match (this)
      {
        | Text(text)
        | Whitespace(text) => text.GetHashCode()
        | Element(ns, _, name, attrs, nodes) => 
          ns.GetHashCode()^ name.GetHashCode() 
            ^ attrs.GetHashCode() ^ nodes.GetHashCode()
      }
    }

    public override Equals(other : ElementBase) : bool
    {
      | node is Node => Equals(node);
      | _            => false
    }

    public Equals(other : Node) : bool
    {
      if (object.ReferenceEquals(this, other)) true
      else
        match (this, other)
        {
          | (Element(ns, _, name, attrs, nodes), Element(ns1, _, name1, attrs1, nodes1)) =>
              name == name1
              && ns.Equals(ns1)
              && attrs.TryForAll2(attrs1, (x, y) => x.Equals(y))
              && nodes.SkipSpaces().TryForAll2(nodes1.SkipSpaces(), (x, y) => x.Equals(y))

          | (Text(left),    Text(right)) => left == right
          | (Whitespace,    Whitespace)  => true
          | _                            => false
        }
    }
  }
}
